﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using MathNet.Spatial.Euclidean;
using MathNet.Spatial.Units;

namespace FDEE.Model
{

    /// <summary>
    /// 多段线型预应力束 -- 适用于PME型盖梁
    /// </summary>
    public class PLTendonRecord : TendonBase
    {
        Angle StAngle, EdAngle;
        private List<Point2D> Vertices;
        private List<Line2D> Segments;
        double Xmax;
        double StForce, EdForce;

        // 预应力伸长量表格 -- 20220112
        DataTable ElongTable;
        // 预应力伸长量表格 -- 20220112

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="tMark">钢束编号</param>
        /// <param name="stage">张拉阶段</param>
        /// <param name="typeStrand">钢束类别</param>
        /// <param name="numStrand">钢绞线根数</param>
        /// <param name="isBothEnd"></param>
        /// <param name="JackForce">张拉力</param>
        public PLTendonRecord(int tMark, int stage, eStrandClass typeStrand, int numStrand, eForceType isBothEnd, double JackForce,
            List<Point2D> pts) :
            base(tMark, stage, typeStrand, numStrand, isBothEnd, JackForce)
        {
            ReportLine = new Dictionary<string, double>();
            Vertices = new List<Point2D>();
            Segments = new List<Line2D>();
            Xmax = 0;
            SetPolyline(pts);
            if (isBothEnd == eForceType.BothEnd)
            {
                StForce = JackingForce * 1000;
                EdForce = JackingForce * 1000;
            }
            else
            {
                StForce = JackingForce * 1000;
                EdForce = 0;
            }
        }


        #region 基本重载
        /// <summary>
        /// 实现伸长量算法
        /// </summary>
        /// <param name="dx"></param>
        public override void SetElongation(ref System.Data.DataTable ElongTable, double dx = 0.01)
        {

            //double E = 196500;
            double E = 197000;
            int npts = (int)(Xmax / dx);
            double dx_real = Xmax / (npts);
            List<double> eff_stress = new List<double>();
            List<double> enlongation = new List<double>();


            double xi, dl;
            double rad_fromSt, rad_fromEd;
            double fp_st, fp_ed;
            for (int i = 0; i < npts; i++)
            {
                xi = i * dx_real + 0.5 * dx_real;
                dl = GetCurveL(xi + 0.5 * dx_real) - GetCurveL(xi - 0.5 * dx_real);
                
                var stress = GetEffectiveStress(xi,out rad_fromSt,out rad_fromEd,out fp_st,out fp_ed);
                eff_stress.Add(stress);
                enlongation.Add(stress * dl  / E*1000);
                DataRow dr = ElongTable.NewRow();
                dr["No"] = string.Format("T{0}", MTendonMark);
                dr["Location"] = xi;  
                dr["k"] = 0.0015;
                dr["μ"] = 0.25;
                dr["θst"] = rad_fromSt;
                dr["θed"] = rad_fromEd;
                dr["fp0_st"] = StForce / Area;
                dr["fp0_ed"] = EdForce / Area;
                dr["fp_st"] = fp_st;
                dr["fp_ed"] = fp_ed;
                dr["fp"] = stress;
                dr["dl"] = dl;
                dr["Ep"] = E;
                dr["△L"] = stress * dl / E * 1000;
                ElongTable.Rows.Add(dr);
            }

            Elongation = Math.Round(enlongation.Sum() * 1e-3, 3, MidpointRounding.AwayFromZero);

            #region 导出计算过程--20211104
            ReportLine.Add("张拉应力", 0);
            ReportLine.Add("伸长量", Elongation);      
            #endregion
        }
        /// <summary>
        /// 实现长度算法
        /// </summary>
        public override void SetLength()
        {
            // 暂不考虑平曲线
            TheoricalLength = GetTheoricalLength();
            double AAnchorReserve; // A锚最小Reserve值
            if (StrandsNum <= 18)
            {
                throw new NotImplementedException();
            }
            else if (StrandsNum <= 22)
            {
                AAnchorReserve = 0.75;
            }
            else if (StrandsNum <= 31)
            {
                AAnchorReserve = 0.85;
            }
            else
            {
                throw new NotImplementedException();
            }

            double AAnchorB; // A锚最小B值
            if (StrandsNum <= 17)
            {
                throw new NotImplementedException();
            }
            else if (StrandsNum <= 22)
            {
                AAnchorB = 0.325;
            }
            else if (StrandsNum <= 27)
            {
                AAnchorB = 0.430;
            }
            else if (StrandsNum <= 31)
            {
                AAnchorB = 0.415;
            }
            else
            {
                throw new NotImplementedException();
            }

            double AAnchorF; // A锚最小F值
            if (StrandsNum <= 19)
            {
                throw new NotImplementedException();
            }
            else if (StrandsNum <= 22)
            {
                AAnchorF = 0.08;
            }
            else if (StrandsNum <= 24)
            {
                AAnchorF = 0.082;
            }
            else if (StrandsNum <= 27)
            {
                AAnchorF = 0.085;
            }
            else if (StrandsNum <= 29)
            {
                AAnchorF = 0.088;
            }
            else if (StrandsNum <= 31)
            {
                AAnchorF = 0.09;
            }
            else
            {
                throw new NotImplementedException();
            }

            double PAnchorB; // P锚最小B值
            if (StrandsNum <= 19)
            {
                throw new NotImplementedException();
            }
            else if (StrandsNum <= 22)
            {
                PAnchorB = 0.9;
            }
            else if (StrandsNum <= 27)
            {
                PAnchorB = 1.00;
            }
            else if (StrandsNum <= 31)
            {
                PAnchorB = 1.1;
            }
            else
            {
                throw new NotImplementedException();
            }
            if (StressType == eForceType.OneEnd)
            {
                // 管道长度=（理论长度）-（A锚B值）-（限位环厚度）-（P锚B值）
                DuctLength = Math.Round(TheoricalLength - AAnchorB - 0.02 - PAnchorB, 3, MidpointRounding.AwayFromZero);
                // 下料长度=0.2（工作长度）+（张拉长度）+（限位块厚度）+（锚头×N）+（理论长度）+（固定端预留）
                Length = Math.Round(0.2 + AAnchorReserve + 0.020 + AAnchorF * 3 + TheoricalLength + 0.2, 3, MidpointRounding.AwayFromZero);
            }
            else
            {
                // 管道长度=（理论长度）-2*（（A锚B值）-（限位环厚度））
                DuctLength = Math.Round(TheoricalLength - 2 * (AAnchorB + 0.02), 3, MidpointRounding.AwayFromZero);
                // 下料长度=2*(0.2（工作长度）+（张拉长度）+（限位块厚度）+（锚头×N）)+（理论长度）
                Length = Math.Round(2 * (0.2 + AAnchorReserve + 0.020 + AAnchorF * 3) + TheoricalLength, 3, MidpointRounding.AwayFromZero);
            }

            #region 导出计算过程--20211104

            ReportLine.Add("曲线长度", 0);
            ReportLine.Add("平直段长度", 0);
            ReportLine.Add("理论长度", TheoricalLength);
            ReportLine.Add("张拉端B值", AAnchorB);
            ReportLine.Add("限位环厚度", 0.02);
            ReportLine.Add("P锚B值", PAnchorB);
            ReportLine.Add("管道长度", DuctLength);
            ReportLine.Add("张拉端工作长度", 0.2);
            ReportLine.Add("张拉端预留长度", AAnchorReserve);
            ReportLine.Add("限位块厚度", 0.020);
            ReportLine.Add("锚头总厚度", AAnchorF * 3);
            ReportLine.Add("固定端预留长度", 0.2);
            ReportLine.Add("钢绞线下料长度", Length);
            ReportLine.Add("角度值", GetDirAngle(0).Degrees);
            #endregion
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 摩擦损失后实际应力
        /// </summary>
        /// <param name="x_meter"></param>
        /// <returns></returns>
        double GetEffectiveStress(double x_meter,out double rad_fromst,out double rad_fromed            ,
            out double fp_st, out double fp_ed)
        {
            double K = 0.0015;
            double mu = 0.25;
            double xst = GetCurveL(x_meter);
            double xed = GetTheoricalLength() - xst;
            rad_fromst = GetAngleAbsChanged_Radians(x_meter, true);
            rad_fromed = GetAngleAbsChanged_Radians(x_meter, false);
            double lossSt = StForce / Area - StForce / Area * (1 - Math.Exp(-(K * xst + mu * GetAngleAbsChanged_Radians(x_meter, true))));
            double lossEd = EdForce / Area - EdForce / Area * (1 - Math.Exp(-(K * xed + mu * GetAngleAbsChanged_Radians(x_meter, false))));
            fp_st = lossSt;
            fp_ed = lossEd;
            return Math.Max(lossEd, lossSt);
        }
        /// <summary>
        /// 角度累计差
        /// </summary>
        /// <param name="x_meter"></param>
        /// <param name="fromStartEnd">是否从起点开始计算</param>
        /// <returns></returns>
        double GetAngleAbsChanged_Radians(double x_meter, bool fromStartEnd = true)
        {
            int idx = GetSegmentIndex(x_meter);
            double res = 0;
            var alist = (from seg in Segments select seg.Direction.AngleTo(Vector2D.XAxis).Radians).ToList();
            if (fromStartEnd)
            {
                for (int i = 0; i < idx; i++)
                {
                    var curA = alist[i];
                    var lastA = i == 0 ? alist[0] : alist[i - 1];
                    res += Math.Abs(curA - lastA);
                }
            }
            else
            {
                alist.Reverse();
                idx = Segments.Count - 1 - idx;
                for (int i = 0; i < idx; i++)
                {
                    var curA = alist[i];
                    var lastA = i == 0 ? alist[0] : alist[i - 1];
                    res += Math.Abs(curA - lastA);
                }
            }

            return res;
        }

        /// <summary>
        /// 第i节线段前进角度
        /// </summary>
        /// <param name="seg_idx"></param>
        /// <returns></returns>
        Angle GetDirAngle(int seg_idx)
        {
            return new Line2D(Vertices[seg_idx], Vertices[seg_idx + 1]).Direction.AngleTo(Vector2D.XAxis);
        }

        /// <summary>
        /// 曲线长
        /// </summary>
        /// <param name="x_meter"></param>
        /// <returns></returns>
        public double GetCurveL(double x_meter)
        {
            double res = 0;
            int idx = GetSegmentIndex(x_meter);
            for (int i = 0; i < idx; i++)
            {
                res += Segments[i].Length;
            }
            double factor = (x_meter - Segments[idx].StartPoint.X) /
                (Segments[idx].EndPoint.X - Segments[idx].StartPoint.X);
            res += factor * Segments[idx].Length;
            return res;
        }


        /// <summary>
        /// 前进节段编号
        /// </summary>
        /// <param name="x_meter"></param>
        /// <returns></returns>
        public int GetSegmentIndex(double x_meter)
        {
            var xs = (from x in Vertices select x.X).ToList();
            if (xs.Contains(x_meter))
            {
                int idx = xs.IndexOf(x_meter);
                if (idx == xs.Count - 1)
                {
                    return idx - 1;
                }
                else
                {
                    return idx;
                }
            }
            else
            {
                xs.Add(x_meter);
                xs.Sort();
                int idx = xs.IndexOf(x_meter);
                if (idx == xs.Count - 1)
                {
                    return idx - 2;
                }
                else
                {
                    return idx-1;
                }
            }
        }


        /// <summary>
        /// 前进角度
        /// </summary>
        /// <param name="x_meter"></param>
        /// <returns></returns>
        public Angle GetDirAngle(double x_meter)
        {
            return GetDirAngle(GetSegmentIndex(x_meter));
        }

        /// <summary>
        /// 设置控制点
        /// </summary>
        /// <param name="pts"></param>
        private void SetPolyline(List<Point2D> pts)
        {
            pts.Sort((x, y) => x.X.CompareTo(y.X));
            foreach (var item in pts)
            {
                Vertices.Add(item);
            }
            var npts = pts.Count();

            for (int i = 0; i < pts.Count - 1; i++)
            {
                Segments.Add(new Line2D(Vertices[i], Vertices[i + 1]));
            }
            StAngle = new Line2D(pts[0], pts[1]).Direction.AngleTo(Vector2D.XAxis);
            EdAngle = new Line2D(pts[npts - 2], pts[npts - 1]).Direction.AngleTo(Vector2D.XAxis);
            Xmax = Vertices.Last().X;
        }
        /// <summary>
        /// 折线长度
        /// </summary>
        /// <returns></returns>
        private double GetTheoricalLength()
        {
            double res = 0;
            for (int i = 0; i < Vertices.Count; i++)
            {
                if (i != 0)
                {
                    res += Vertices[i].DistanceTo(Vertices[i - 1]);
                }
            }
            return Math.Round(res, 3, MidpointRounding.AwayFromZero);
        }
        #endregion



    }
}
